#region Copyright Information
/*
 * (C)  2005-2007, Marcello Cauchi Savona
 *
 * For terms and usage, please see the LICENSE file
 * provided alongwith or contact marcello_c@hotmail.com
 * http://www.cheekyneedle.com
 * 
 * 
 */
#endregion


/*
 * clsUDP
 * shall start a listner, and raise an event every time data arrives on a port
 * shall also be able to send data via udp protocol
 * .Dispose shall remove all resources associated with the class
 */


using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using Microsoft.Win32;


namespace IntegratedOnBordDebug
{
    public class clsUDP

    {
        #region "Class Variables"
        private int PortToListenTo, PortToSendTo = 0;
        private string localRecvIP;
        public bool IsListening;
        // call backs for send/recieve!
        public UdpState udpState;
        #endregion

        #region "Class Events"
        public delegate void DataRcvdEventHandler(byte[] DData, IPEndPoint RIpEndPoint);
        public event DataRcvdEventHandler DataRcvd;
        public delegate void ErrEventHandler(string Msg);
        #endregion

        //class constructors
        public clsUDP()
        {
            IsListening = false;
        }
        //overrides pass the port to listen to/sendto and startup
        public clsUDP(int PortToListenTo, int PortToSendTo, string rcvCardIP)
        {
            try
            {
                IsListening = false;
                this.PortToListenTo = PortToListenTo;
                this.PortToSendTo = PortToSendTo;
                this.localRecvIP = rcvCardIP;
                StartListener();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);

            }
        }

        //string property to contain the class name
        private string ClassName
        {
            get
            {
                return "clsUDP";
            }
        }

        //function to send data as a byte stream to a remote socket
        // modified to work as a callback rather than a block
        public void SendData(byte[] Data)
        {
            try
            {
                udpState.udpClient.BeginSend(Data, Data.Length, "255.255.255.255", PortToSendTo, new AsyncCallback(OnDataSent), udpState);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>p2p send data
        /// 
        /// </summary>
        /// <param name="IpAddress"></param>
        /// <param name="Data"></param>
        public void SendData(string IpAddress, byte[] Data)
        {
            try
            {
                if (string.IsNullOrEmpty(IpAddress))
                {
                    udpState.udpClient.BeginSend(Data, Data.Length, "255.255.255.255", PortToSendTo, new AsyncCallback(OnDataSent), udpState);
                }
                else
                {
                    udpState.udpClient.BeginSend(Data, Data.Length, IpAddress, PortToSendTo, new AsyncCallback(OnDataSent), udpState);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }


        // This is the call back function, which will be invoked when a client is connected
        public void OnDataSent(IAsyncResult asyn)
        {

            try
            {
                //get the data
                UdpClient ii = (UdpClient)asyn;
                ii.EndSend(asyn); // stop the send call back
            }
            catch (Exception ex)
            {
                if (IsListening == true)
                    Console.WriteLine(ex.Message);
            }
        }



        //function to start the listener call back everytime something is recieved
        private void IniListnerCallBack()
        {
            try
            {
                // start teh recieve call back method
                udpState.udpClient.BeginReceive(new AsyncCallback(OnDataRecieved), udpState);
            }
            catch (Exception ex)
            {
                if (IsListening == true)
                    Console.WriteLine(ex.Message);
            }
        }


        // This is the call back function, which will be invoked when a client is connected
        public void OnDataRecieved(IAsyncResult asyn)
        {
            byte[] receiveBytes;
            UdpClient u;
            IPEndPoint e;

            try
            {

                u = ((UdpState)(asyn.AsyncState)).udpClient;
                e = ((UdpState)(asyn.AsyncState)).ipEndPoint;

                receiveBytes = u.EndReceive(asyn, ref e);
                //raise the event with the data recieved
                DataRcvd?.Invoke(receiveBytes, e);
            }
            catch (Exception ex)
            {
                if (IsListening == true)
                    Console.WriteLine(ex.Message);
            }
            finally
            {
                u = null;
                e = null;
                receiveBytes = null;
                // recall the call back
                IniListnerCallBack();
            }

        }


        //function to start the listener 
        //if the the listner is active, destroy it and restart
        // shall mark the flag that the listner is active
        private void StartListener()
        {
            // byte[] receiveBytes; // array of bytes where we shall store the data recieved
            IPAddress ipAddress;
            IPEndPoint ipLocalEndPoint;


            try
            {

                IsListening = false;
                //resolve the net card ip address
                ipAddress = string.IsNullOrEmpty(localRecvIP) ? IPAddress.Any : IPAddress.Parse(localRecvIP);
                localRecvIP = ipAddress.ToString();

                //get the ipEndPoint
                ipLocalEndPoint = new IPEndPoint(ipAddress, PortToListenTo);
                // if the udpclient interface is active destroy
                if (udpState.udpClient != null) udpState.udpClient.Close();
                udpState.udpClient = null; udpState.ipEndPoint = null;
                //re initialise the udp client

                udpState = new UdpState();
                udpState.ipEndPoint = ipLocalEndPoint;
                udpState.udpClient = new UdpClient(ipLocalEndPoint);
                if (PortToListenTo == 0)
                    PortToListenTo = ((IPEndPoint)udpState.udpClient.Client.LocalEndPoint).Port;

                //udpState.udpClient.Client.Bind(ipLocalEndPoint);

                IsListening = true; // set to start listening
                // wait for data
                IniListnerCallBack();
            }
            catch (Exception ex)
            {
                if (IsListening == true)
                    Console.WriteLine(ex.Message);
            }
            finally
            {
                if (udpState.udpClient == null)
                {
                    Thread.Sleep(1000);
                    StartListener();
                }
                else
                {
                    ipAddress = null;
                    ipLocalEndPoint = null;
                }
            }
        }

        //stop the listener thread
        public void StopListener()
        {
            try
            {
                IsListening = false;
                if (udpState.udpClient != null) udpState.udpClient.Close();
                udpState.udpClient = null; udpState.ipEndPoint = null;

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        //dispose of all resources
        ~clsUDP()
        {
            try
            {
                StopListener();
                if (udpState.udpClient != null) udpState.udpClient.Close();
                udpState.udpClient = null; udpState.ipEndPoint = null;
                localRecvIP = null;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        //class that shall hold the reference of the call backs
        public struct UdpState
        {
            public IPEndPoint ipEndPoint; //define an end point
            public UdpClient udpClient; //define a client
        }

        public bool SetLedLightOnAll(string IpAddress, int LedType, int keepTime = 10)
        {
            bool flag = false;
            byte[] buff = new byte[] { 0xB6, 0x6B, 0xFF, 0xBB, 0x33, 0x00, 0x00, 0x00, 0xE7, 0x03, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xC5, 0xB0, 0xB3, 0x02, (byte)LedType, (byte)keepTime, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            SendData(IpAddress, buff);
            return flag;
        }
    }
}
